#include <iostream>
#include <fstream>
#include <cstdlib>
#include <cstring>
class Image;
class Mask;

class Array2d {
protected:
    int** data;    //store grey value for every pixel
    int32_t h, w;  //Image high and width  //base 0
public:
    Array2d(): data(nullptr), h(-1), w(-1) {}
    Array2d(const Array2d& other) { //copy constructor
        h=other.h;
        w=other.w;
        create(h,w);
        for(int i=0; i<h; i++)
            for(int j=0; j<w; j++)
                data[i][j]=other.data[i][j];
    }
    ~Array2d() {
        for(int i=0; i<h; i++)
            delete []data[i];
        delete []data;
    }
    void create(const int32_t hh, const int32_t ww) { //allocate space for Array2d
        h=hh; w=ww;
        data = new int*[hh];
        for(int i=0; i<hh; i++)
            data[i] = new int[ww];
    }
};

class Image: public Array2d {
public:
    Image() {}        
    Image(const Array2d& array2):Array2d(array2){}
    Image(const Image& image){ //copy constructor
        if(data!=nullptr){ //in case **data has been allocated
           for(int i=0; i<h; i++)
                delete []data[i];
            delete []data;         
        }
        h=image.h;
        w=image.w;
        create(h,w);
        for(int i=0; i<h; i++)
            for(int j=0; j<w; j++)
                data[i][j]=image.data[i][j];
    }
    Image(const char* filename) {  //read the grey value of each pixel and store into the newly constructed Image
        std::ifstream infille;
        infille.open(filename);
        if(!infille) {std::cerr<<"open file error\n"; return;}

        int h1, w1;
        infille>>h1>>w1;
        create(h1, w1);  //allocate space
        for(int i=0; i<h; i++)   //read and put in datas
            for(int j=0; j<w; j++)
                infille>>data[i][j]; 
        infille.close();
    }
    ~Image() {}

    Image& operator=(const Image& image){ 
        if(this==&image) return *this;
        if(data!=nullptr){ //in case **data has been allocated
           for(int i=0; i<h; i++)
                delete []data[i];
            delete []data;         
        }
        h=image.h;
        w=image.w;
        create(h,w);
        for(int i=0; i<h; i++)
            for(int j=0; j<w; j++)
                data[i][j]=image.data[i][j];
        return *this;
    }

    Image equalizeHist() const {
        int a[256]={}, b[256]={};
        for(int i=0; i<h; i++)
            for(int j=0; j<w; j++)
                a[data[i][j]]++; //a[] is used to form the orignal Histogram
        int n=h*w;
        b[0]=255*double(a[0])/n; //b[] is used to store the Histo-equalized grey value in a[]
        for(int i=0; i<=255; i++){
            for(int j=0; j<=i; j++)
                b[i]+=a[j];
                b[i]=int(255*(double(b[i])/double(n)));
        }

        Image hist;
        hist.create(h,w);
        for(int i=0; i<h; i++)
            for(int j=0; j<w; j++)
                hist.data[i][j] = b[data[i][j]]; 
        return hist;
    }
    void drawHist(const char* filename) {    
        //open "filename" and put the grey-level histogram in
        int a[256]={};
        for(int i=0; i<h; i++)
            for(int j=0; j<w; j++)
                a[data[i][j]]++;   
                 
        std::ofstream outfile(filename);
        if(!outfile) {std::cerr<<"create file error\n"; return;}        
        for(int i=1; i<=255; i++){
            if(i<10) outfile<<i<<"   "<<"|";
            else if(i>=10 && i<100) outfile<<i<<" "<<"|";
            else outfile<<i<<"|";
            for(int j=1; j<=a[i]; j++)outfile<<"=";
            outfile<<std::endl;
        }
        outfile.close();
    }
    void dump2File(const char* filename = "out.txt") const {  //create and put each pixel's grey level in "filename"
        std::ofstream outfile(filename);
        if(!outfile) {std::cerr<<"create file error\n"; return;}
        outfile<<h<<" "<<w<<std::endl;
        for(int i=0; i<h; i++)
            for(int j=0; j<w; j++)
                outfile<<data[i][j]<<'\n';
        outfile.close();
    }
    friend class Mask;
    Image operator *(const Mask& mask) const;
};

class Mask: public Array2d {
public:                              //忘了public,所以有输入形参时:以声明,不可访问
    Mask() = default;
    Mask(const int i=1):Array2d() {
        create(3,3);
        if(i==1){
            data[0][0]=data[0][2]=data[2][0]=data[2][2]=data[0][1]=data[1][0]=data[1][2]=data[2][1]=1;
            data[1][1]=-8;
        }
        if(i==2){
            data[0][0]=data[0][2]=data[2][0]=data[2][2]=data[0][1]=data[1][0]=data[1][2]=data[2][1]=-1;
            data[1][1]=9;
        }
        if(i==3){
            data[0][0]=data[2][0]=-1;
            data[1][0]=-2;
            data[0][1]=data[1][1]=data[2][2]=0;
            data[0][2]=data[2][2]=1;
            data[1][2]=2;
        }
        if(i==4){
            data[0][0]=data[0][2]=data[2][0]=data[2][2]=0;
            data[0][1]=data[1][0]=data[1][2]=data[2][1]=-1;
            data[1][1]=5;           
        }
    }
    Mask(int i1, int i2, int i3, int i4, int i5, int i6, int i7, int i8, int i9){
        create(3,3);
        data[0][0]=i1; data[0][1]=i2; data[0][2]=i3;
        data[1][0]=i4; data[1][1]=i5; data[1][2]=i6; 
        data[2][0]=i7; data[2][1]=i8; data[2][2]=i9; 
    }
    ~Mask() {};

    friend Image;
    Image operator *(const Image& image) const {
        Image image1;
        image1 = image*(*this);
        return image1;
    }
};

Image Image::operator *(const Mask& mask) const { //use operator* to achieve Convolution
    Image image;
    image.create(h,w);
    int tmp, a[3]={-1, 0, 1},n=0,i,j;
    for(i=0; i<=2; i++)
        for(j=0; j<=2; j++)
            n+= mask.data[i][j];
    for( i=0; i<h; i++)
        for( j=0; j<w; j++){
            tmp=0;                 //caculate convolution
            for(int k1=0; k1<=2; k1++)
                for(int k2=0; k2<=2; k2++){
                    if(i+a[k1]<0 || i+a[k1]>=h || j+a[k2]<0 || j+a[k2]>=w);
                    else tmp+=data[i+a[k1]][j+a[k2]]*mask.data[k1][k2];
                }
            if(n!=0) image.data[i][j] = tmp/n;
                else image.data[i][j] = tmp;
        }
    if(n==0)
        for(int i=0; i<h; i++)
            for(int j=0; j<w; j++)
                image.data[i][j]+=data[i][j];
    return image;
}